package net.esj.basic.core.engine.provider;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javassist.expr.Instanceof;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import net.esj.basic.core.engine.factory.bean.BeanFactory;
import net.esj.basic.exception.MelonException;
import net.esj.basic.utils.ObjectUtils;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public abstract class AbstractProvidersFactory<I extends IProvider> implements IProvidersFactory<I> {

	private BeanFactory beanFactory;
	
	protected Collection<I> listProviders(){
		Map<String,I> map = beanFactory.getBeansByType(getProviderClass());
		if(map.isEmpty()){
			throw new MelonException("interface:["+getProviderClass().getName()+"] must be instantiation");
		}
		List<I> reval =new ArrayList<I>(map.values());
		Collections.sort(reval,new Comparator<I>() {

			@Override
			public int compare(I o1, I o2) {
				return o1.getOrder()-o2.getOrder();
			}
		});
		return reval;
	}
	
	protected abstract Class<I> getProviderClass(); 
	
	protected abstract void provideProxy(I proxy);
	
	@PostConstruct
	private void init(){
		Enhancer enhancer = new Enhancer(); 
		enhancer.setInterfaces(new Class[]{getProviderClass()});
		enhancer.setCallback(new BasicMethodInterceptor());
		provideProxy((I) enhancer.create());
	}

	/*public I getProxy(){
		return (I) proxy;
	}*/
	
	@Inject
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}
	
	private class BasicMethodInterceptor implements MethodInterceptor{

		@Override
		public Object intercept(Object arg0, Method arg1, Object[] arg2,
				MethodProxy arg3) throws Throwable {
			Object retal = null;
			for(I i: listProviders()){
				try{
					retal = arg1.invoke(i, arg2);
				}catch(ProviderRuntimeException e){
				}
				return retal;
				/*if(!ObjectUtils.isEmpty(retal)){
					return retal;
				}*/
			}
			return retal;
		}
		
	}
	
}
